﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX.DirectInput;
using D3D = Microsoft.DirectX.Direct3D;

namespace Sobel
{
    public partial class WinForm : Form
    {
        private D3D.Device device;  //tu pierwsza nowość. Zmienna typu Device.

        private MyObject O;
        private System.ComponentModel.Container components = null;
        private Microsoft.DirectX.DirectInput.Device keyboard;

        private MyCamera Camera;

        float time = 0.0f, time2 = 0.0f;

        private D3D.Font Font3D;

        private float fps;
        private int frames = 0, PrevTickCount = 1;

        private D3D.Effect effect;

        private Matrix mProjection, mView;

        private Vector4 LightPosition;

        private Texture sceneTexture; // render scene to this texture
        private RenderToSurface renderToSurface;

        private Texture properTexture; // put this texture on detected edges
        private Texture mountainDewTexture;

        private Vector4 MaterialAmbient;
        private Vector4 MaterialEmissive;
        private Vector4 MaterialDiffuse;
        private Vector4 MaterialSpecular;
        private float MaterialPower;
        private Vector4 DirLightColor;
        private int TexWidth;
        private int TexHeight;

        private CustomVertex.TransformedTextured[] triangles;

        public WinForm()  // konstruktor - inicjalizujemy komponenty formy (tak naprawdę to ich nie ma, ale wygodniej się pracuje w VS)
        {
            InitializeComponent();
            LightPosition = new Vector4(0.0f, 5.0f, 1.0f, 1.0f);
            MaterialAmbient = new Vector4(0.0f, 0.1f, 0.1f, 1.0f);
            MaterialEmissive = new Vector4(0, 0, 0, 1);
            MaterialDiffuse = new Vector4(0.0f, 0.9f, 0.9f, 1.0f);
            MaterialSpecular = new Vector4(0.2f, 0.2f, 0.2f, 1.0f);
            MaterialPower = 20.0f;
            DirLightColor = new Vector4(1.0f, 1.0f, 1.0f, 1.0f);
        }

        public void InitializeKeyboard()
        {
            keyboard = new Microsoft.DirectX.DirectInput.Device(SystemGuid.Keyboard);
            keyboard.SetCooperativeLevel(this, CooperativeLevelFlags.Background | CooperativeLevelFlags.NonExclusive);
            keyboard.Acquire();
        }

        private void UpdateFramerate()
        {
            frames++;
            if (Math.Abs(Environment.TickCount - PrevTickCount) > 1000)
            {
                fps = (float)frames * 1000 / Math.Abs(Environment.TickCount - PrevTickCount);
                PrevTickCount = Environment.TickCount;
                frames = 0;
            }

        }

        public void InitializeMeshes()
        {
            O = new MyObject(ref device);

            O.LoadFromFile("D:\\SSStudia\\mgr Semestr II\\3d\\x\\tbomb.x");
            O.Normalize(Matrix.RotationX((float)Math.PI / 2));
            O.NormalizeVB();

            Camera = new MyCamera();

            //prepare texture to render the scene, which will be a base to edge detection

            sceneTexture = new Texture(device, TexWidth, TexHeight, 1, Usage.RenderTarget, Format.X8R8G8B8, Pool.Default);
            properTexture = TextureLoader.FromFile(device, "D:\\SSStudia\\mgr Semestr II\\3d\\x\\Cedfence.bmp");
            mountainDewTexture = TextureLoader.FromFile(device, "D:\\SSStudia\\mgr Semestr II\\3d\\MountainDew.jpg");
        }

        public void UpdateCamera()
        {
            Camera.Position = new Vector3(O.radius * 5.0f * (float)Math.Sin(time), O.radius * 5.0f * (float)Math.Sin(time2), -O.radius * 5.0f * (float)Math.Cos(time));
            //Camera.Position = new Vector3(0.0f, 5.0f, 5.0f);
            Camera.Up = new Vector3(0.0f, 1.0f, 0.0f);
            float aspect_ratio = this.Width / (float)this.Height;
            mProjection = Matrix.PerspectiveFovLH((float)Math.PI / 4, aspect_ratio, 0.02f, O.radius * 10);  // macierz projekcji
            mView = Camera.ViewMatrix(O.center);
        }

        public void UpdateLights()
        {
            LightPosition = new Vector4(0.0f, -50.0f, -50.0f, 1.0f);

        }

        public void InitializeDevice() // Druga nowość. Inicjujemy urządzenie.
        {
            PresentParameters presentParams = new PresentParameters();
            presentParams.Windowed = true;
            presentParams.SwapEffect = SwapEffect.Discard;
            presentParams.AutoDepthStencilFormat = DepthFormat.D16;
            presentParams.EnableAutoDepthStencil = true;
            device = new D3D.Device(0, D3D.DeviceType.Hardware, this, CreateFlags.HardwareVertexProcessing, presentParams);
            device.RenderState.CullMode = Cull.None;
            //parameters to adjust texture size to backbuffer's size
            PresentParameters deviceParams = device.PresentationParameters;
            TexWidth = deviceParams.BackBufferWidth;
            TexHeight = deviceParams.BackBufferHeight;
            System.Drawing.Font Font2D = new System.Drawing.Font("Arial", 12f, FontStyle.Regular);
            Font3D = new D3D.Font(device, Font2D);

            float x = this.Width;
            float y = this.Height;
            triangles = new CustomVertex.TransformedTextured[6];
            triangles[0] = new CustomVertex.TransformedTextured(0f, 0f, 0f, 1f, 0.0f, 0.0f);
            triangles[1] = new CustomVertex.TransformedTextured(x, 0f, 0f, 1f, 1.0f, 0.0f);
            triangles[2] = new CustomVertex.TransformedTextured(x, y, 0f, 1f, 1.0f, 1.0f);
            triangles[3] = new CustomVertex.TransformedTextured(x, y, 0f, 1f, 1.0f, 1.0f);
            triangles[4] = new CustomVertex.TransformedTextured(0f, y, 0f, 1f, 0.0f, 1.0f);
            triangles[5] = new CustomVertex.TransformedTextured(0f, 0f, 0f, 1f, 0.0f, 0.0f);


            effect = D3D.Effect.FromFile(device, "2a.fx", null, ShaderFlags.Debug, null);
            renderToSurface = new RenderToSurface(device, TexWidth, TexHeight, Format.X8R8G8B8, true, DepthFormat.D16);
        }

        private void ReadKeyboard()
        {
            KeyboardState keys = keyboard.GetCurrentKeyboardState();


            if (keys[Key.Left])
            {
                time += 0.01f;
            }
            if (keys[Key.Right])
            {
                time -= 0.01f;
            }

            if (keys[Key.Up])
            {
                time2 += 0.01f;
            }
            if (keys[Key.Down])
            {
                time2 -= 0.01f;
            }
        }




        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) //Trzecia nowość. W zdarzeniu OnPaint będziemy rysować. 
        {

            ReadKeyboard();
            UpdateLights();
            UpdateCamera();
            UpdateFramerate();

            int passes;


            Surface surface = sceneTexture.GetSurfaceLevel(0);
            renderToSurface.BeginScene(surface);

            device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.DarkSlateBlue, 1.0f, 0);
            //device.BeginScene(); // początek sceny 
            effect.Technique = "lighted";
            effect.SetValue("xWorld", Matrix.Identity);
            effect.SetValue("xView", mView);
            effect.SetValue("xProjection", mProjection);
            effect.SetValue("xLightPosition", LightPosition);
            effect.SetValue("xViewPosition", new Vector4(Camera.Position.X, Camera.Position.Y, Camera.Position.Z, 1.0f));
            effect.SetValue("materialAmbient", MaterialAmbient);
            effect.SetValue("materialEmissive", MaterialEmissive);
            effect.SetValue("materialDiffuse", MaterialDiffuse);
            effect.SetValue("materialSpecular", MaterialSpecular);
            effect.SetValue("materialPower", MaterialPower);
            effect.SetValue("dirLightColor", DirLightColor);

            device.SetStreamSource(0, O.vb, 0);
            device.VertexDeclaration = O.vd;
            effect.CommitChanges();

            passes = effect.Begin(0);
            for (int i = 0; i < passes; i++)
            {
                effect.BeginPass(i);
                O.Render(ref effect, null);
                effect.EndPass();
            }
            effect.End();
            renderToSurface.EndScene(Filter.None);
            //device.EndScene();
            //Sprite s = new Sprite(device);

            //TextureLoader.Save("D:\\tex.bmp", ImageFileFormat.Bmp, sceneTexture);

            effect.Technique = "edgeDetector";
            effect.SetValue("xWholeScene", sceneTexture);
            effect.SetValue("xProperTexture", properTexture);

            effect.SetValue("xWorld", Matrix.Identity);
            effect.SetValue("xView", mView);
            effect.SetValue("xProjection", mProjection);
            effect.SetValue("xLightPosition", LightPosition);
            effect.SetValue("xViewPosition", new Vector4(Camera.Position.X, Camera.Position.Y, Camera.Position.Z, 1.0f));
            effect.SetValue("materialAmbient", MaterialAmbient);
            effect.SetValue("materialEmissive", MaterialEmissive);
            effect.SetValue("materialDiffuse", MaterialDiffuse);
            effect.SetValue("materialSpecular", MaterialSpecular);
            effect.SetValue("materialPower", MaterialPower);
            effect.SetValue("dirLightColor", DirLightColor);

            effect.CommitChanges();

            device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1.0f, 0);
            device.BeginScene();
            device.VertexFormat = CustomVertex.TransformedTextured.Format;
            passes = effect.Begin(0);
            for (int i = 0; i < passes; i++)
            {
                effect.BeginPass(i);
                device.DrawUserPrimitives(PrimitiveType.TriangleList, 2, triangles);
                effect.EndPass();
            }
            effect.End();
            device.EndScene();  //koniec sceny

            Font3D.DrawText(null, string.Format("{0:0.00} FPS", fps), new Point(10, 10), Color.White);
            Font3D.DrawText(null, string.Format("{0:0.00}, {1:0.00}, {2:0.00} Camera", Camera.Position.X, Camera.Position.Y, Camera.Position.Z), new Point(10, 25), Color.White);
            Font3D.DrawText(null, string.Format("{0:0.00}, {1:0.00}, {2:0.00} Light", LightPosition.X, LightPosition.Y,LightPosition.Z), new Point(10, 40), Color.White);
            device.Present(); //tu informujemy, że Device już jest gotowe.
            this.Invalidate(); //to po to aby windows przerysował formę
            //TextureLoader.Save("D:\\texture.bmp", ImageFileFormat.Bmp, sceneTexture);
        }

        protected override void Dispose(bool disposing) // tu zwalniamy wszystkie komponenty
        {
            if (disposing)
            {
                if (components != null)
                {
                    components.Dispose();
                }
            }
            base.Dispose(disposing);
        }
        private void InitializeComponent() // tu metoda inicjująca komponenty
        {
            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true); //tu drugi dodatek, aby Windows dobrze przerysowywał okno.

            this.ClientSize = new System.Drawing.Size(800, 600);
            this.Name = "WinForm";
            this.Text = "simple shaders";


        }

        static void Main()  // tu uruchamiamy aplikację.
        {
            using (WinForm dx_form = new WinForm())
            {
                dx_form.InitializeKeyboard();
                dx_form.InitializeDevice(); // Czwarta nowość. Trzeba zainicjować Device
                dx_form.InitializeMeshes();
                Application.Run(dx_form);
            }
        }


    }
}
